#####################################################################
# Product:      KnowledgeEngine (Plone Edition)
#
# File:         DatetimeSelectorInfo.py
#
# Copyright:    Copyright (c) 2006, Faulkner Technologies
#
# Author:       Tinh Truong
#
# Description:  A DatetimeSelector is a simple Component that permits capturing
#               of date time information during the process of assessment
#####################################################################

#####################################################################
## Knowledge Engine Library Imports
from Products.KnowledgeEngine.Common import *
from Products.KnowledgeEngine.Core import *
from Products.KnowledgeEngine.Component import Component, ComponentInfo
from Products.KnowledgeEngine.Assessible import Assessible
from Products.KnowledgeEngine.AssessmentComponentState import AssessmentComponentState
from Products.KnowledgeEngine.AssessmentComponentValueTypes import *
from Products.KnowledgeEngine.Exportable import Exportable
from Products.KnowledgeEngine.KERLExtensions import KERLExtensions
from Products.KnowledgeEngine.PropertyDefinition import *
from Products.KnowledgeEngine.PropertyTypes import *
from Products.KnowledgeEngine.PropertyFormats import *
from Products.KnowledgeEngine.PropertyStructures import *
from Products.KnowledgeEngine.KERFProcessor import *
from Products.KnowledgeEngine.KERLProcessor import InvalidKerlOperator

#####################################################################
## Zope Library Imports
from Globals import DTMLFile
from ImageFile import ImageFile
from Products.Archetypes.public import *
from DateTime.DateTime import DateTime, DateTimeError

#####################################################################
## Python Library Imports
# (none)

#####################################################################
## Exceptions
class DatetimeSelectorException( ValidationException ): pass
class InvalidDateException( DatetimeSelectorException ): pass


#####################################################################
## TextBoxInfo Class Definition
class DatetimeSelectorInfo(ComponentInfo):
    """
        Defines the class info methods, defining information about
        the class without needing an instance of the class
    """

    #---------------------------------------------------------------#
    
    def getClassName(self):
        """
            Return the class name
        """
        
        return 'DatetimeSelector'

    #---------------------------------------------------------------#

    def getDisplayName(self):
        """
            Return a friendly name for the class instances
            (this is used for displaying in lists etc)
        """
    
        return "Date & Time Question"
        
    #---------------------------------------------------------------#

    def getIdentityType(self):
        """
            Return the identity type.  
        """
        
        return "datetimeselector"
        
    #---------------------------------------------------------------#
    
    def getClassIcon(self):
        """
            Return the appropriate icon file for the class.
        """
        
        return ImageFile('skins/DatetimeSelector_icon.gif', globals())
        

    #---------------------------------------------------------------#

    def isInstanceOf(self, className):
        """
            Returns if the class is an instance of the specified
            className, or implements the interface specified 
            by the className
        """        
        return className in ('DateTime','DateTimeSelector','Assessible', 'Exportable') or ComponentInfo.isInstanceOf(self, className)
    
    #---------------------------------------------------------------#

    def onConfigureProperties(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register 
            class specific properties using the PropertyReflection interface.
            
            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered
            
            NOTE: This method is called before ClassInfo.onAfterStartup()            
        """
        #initialise the super-class properties for reflection
        ComponentInfo.onConfigureProperties(self)
        
        self.addPropertyDefinition(PropertyDefinition(
            identity="yearMin", 
            displayName="Start Year",
            description="Enter the starting year",
            defaultValue="1901",
            propertyType=pt_STRING,
            propertyFormat=pf_TEXT_SINGLELINE,
        ))
        
        self.addPropertyDefinition(PropertyDefinition(
            identity="yearMax", 
            displayName="End Year",
            description="Enter the ending year",
            defaultValue="2020",
            propertyType=pt_STRING,
            propertyFormat=pf_TEXT_SINGLELINE,
        ))       
        
        self.addPropertyDefinition(PropertyDefinition(
            identity="showTime", 
            displayName="Show Time",
            description="Should the time be shown?",
            defaultValue=False,
            propertyType=pt_BOOLEAN,
            propertyFormat=pf_CHECKBOX,
        ))
        
        
        
        #add the 'reportContent' property        
        self.addPropertyDefinition(PropertyDefinition(identity="reportContent", displayName="Report Content", description="Text to be displayed for this component when used in a report", defaultValue="", propertyType=pt_STRING, propertyFormat=pf_HTML, propertyStructure=ps_ATOMIC))        
        
        self.addPropertyDefinition(PropertyDefinition(identity="defaultDatetime", displayName="Default DateTime", defaultValue='', description="Set the default datetime (eg. set the date to today 'now' or set 30 day from now 'now + 30'", propertyFormat=pf_TEXT_SINGLELINE))


    #---------------------------------------------------------------#

#####################################################################
## DatetimeSelector Class Definition
class DatetimeSelector(Component, Assessible, KERLExtensions, Exportable):
    """
        Defines a simple component to represent one or more lines of 
        text entered during an assessment.
    """

    #---------------------------------------------------------------#

    #TRANSIENT: a ClassInfo implementation instance to provide info about this class
    _CLASSINFO = DatetimeSelectorInfo()
    
    #---------------------------------------------------------------#

    thispath = "/".join(globals()['__file__'].split("/")[:-1])
    jsFile = open(thispath + '/skins/DatetimeSelector.js','r')
    JAVASCRIPT_DECLARATIONS = jsFile.read()
    jsFile.close()

    # DTML Methods
    index_html = DTMLFile('dtml/index_html', globals())
    simplereport_html = DTMLFile('dtml/simplereport_html', globals())

    #---------------------------------------------------------------#

    def __init__(self, id, identity):
        "Constructor for the DatetimeSelector"        
        
        Component.__init__(self, id, identity)

    #---------------------------------------------------------------#
    
    def getDefaultDatetime(self):
        "Returns the component assessment default value"         
        
        return self.convertDateTime( self.defaultDatetime )

    #---------------------------------------------------------------#
    
    def setDefaultDatetime(self, defaultDatetime):
        "Returns the component assessment default value"         
        
        self.defaultDatetime = defaultDatetime
    #---------------------------------------------------------------#
    
    def getStartYear(self):
        """ Returns the starting year for this component """
        
        return int(getattr(self.aq_explicit, 'yearMin', 1990))
    
    def getEndYear(self):
        """ Returns the starting year for this component """
        
        return int(getattr(self.aq_explicit, 'yearMax', 2020))
    
    def getShowTime(self):
        """ Returns the starting year for this component """
        
        return getattr(self.aq_explicit, 'showTime', False)    
    
    def getAvailableYears(self):
        """ 
            Returns the available years
            TODO: Should be a configurable property
        """
        
        return range(self.getStartYear(),self.getEndYear() + 1)
    
    def getAvailableMonths(self):
        """ 
            Returns the available months
        """
        
        return [(1,  "January"),
                (2,  "February"),
                (3,  "March"),
                (4,  "April"),
                (5,  "May"),
                (6,  "June"),
                (7,  "July"),
                (8,  "August"),
                (9,  "September"),
                (10, "October"),
                (11, "November"),
                (12, "December")]
                
                    
    
    
    #---------------------------------------------------------------#

    def onAfterIdentityChange(self, identityMappings):
        """
            Handle Knowledge Engine specific actions after the 
            identity of this object has changed.
            
            The identityMappings dictionary holds 
            mappings from oldIdentities -> newIdentities
            (including this object)
                        
        """
        #perform default identity changes
        Component.onAfterIdentityChange(self, identityMappings)

    #---------------------------------------------------------------#

    def onAfterInstanciation(self):
        """
            Handle Knowledge Engine specific actions after this object 
            has been loaded into memory.  
            
            This is useful for performing backwards compatibility
        """
        #perform default onAfterInstanciation for the parent
        Component.onAfterInstanciation(self)
        
        #add maximumLength attribute if it doesn't already exist
        if not hasattr(self, "defaultDatetime"):
            self.defaultDatetime = "now"
        
    #---------------------------------------------------------------#
        
    def getComponentProperty(self, identity, propertyName, assessment = None):
        """
            Returns the value of the specified property, given the
            identity of the component (which may be used to lookup states) and
            an assessment.

            RETURNS: None if the property doesn't exist
        """
        if propertyName == "value":
            result = self.toLocalizedTime(assessment.getComponentState(identity).getValue()) 
            if result:
                return result
            else:
                return "(No Date)"
        else:
            return Component.getComponentProperty(self, identity, propertyName, assessment)
       
    #---------------------------------------------------------------#
    # BEGIN: Assessible Implementation                              #
    #---------------------------------------------------------------#
        
    def getAssessmentValueType(self):
        """
            ABSTRACT: Returns the type of the value that is to be stored
            for this component when it's assessment state is persisted.
            
            NOTE 1: See AssessmentComponentValueTypes for a list of defined contants       
            NOTE 2: This method MUST be overriden in the implementing
            class to specify the correct type for value persistence
            
            RETURN TYPE: String
        """
        #textbox assessment values are strings
        return st_STRING
        
    #---------------------------------------------------------------#
        
    def convertDateTime(self, value):
        """
            Convert a string to the appropriate date and time
           
            NOTE: the possible entered value are 
                  'now' - which represent the current date and time
                  'now+10' - in 10days time
        """ 
        
        # jamesd 20060721 - TODO: Could be a bit more robust.
        # ... Implement php strftime() style functionality
        
        dateTime = ""
        valueComponent = None

        #value = self.defaultDatetime        
        #dateTime = d.strftime("%Y-%m-%d %I:%M %p")              
        
        if len(value) > 0:
            if '+' in value:
                valueComponent = value.split('+')
                d = DateTime() + valueComponent[1]              
            elif '-' in value:
                valueComponent = value.split('-')
                d = DateTime() - valueComponent[1]
            elif value.strip() == 'now':
                d = DateTime()              
            else:
                d = DateTime(value)
              
            return d
        else:
            return None            
    #---------------------------------------------------------------#
        
    def getDefaultAssessmentState(self, assessment):
        """
            ABSTRACT: Constructs and returns a suitable 'default' 
            AssessmentComponentState for the provided Assessment
            instance.

            NOTE: This method MUST be overriden in the implementing
            class to provide a suitable default value for the component
            
            RETURN TYPE: AssessmentComponentState
        """
        

        
        return AssessmentComponentState(assessment.getIdentity(), assessment.getModelIdentity(), self.getIdentity(), self.getAssessmentValueType(), self.getDefaultDatetime(), revision=assessment.getRevision(), searchIndex=self.getSearchIndex())
            
    #---------------------------------------------------------------#

    def updateAssessmentState(self, assessment, formValue):
        """
            ABSTRACT: Updates the specified assessment using
            the provided HTTP REQUEST formValue.
            
            NOTE: This method MUST be overriden in the implementing
            class to suitably update the component state
        """
        Component.updateAssessmentState( self, assessment, formValue )

        try:
            # If it's set, Try and parse the date string to make sure it's valid
            if formValue:
                dateValue = DateTime( formValue )
        except DateTimeError:
            # Date string not valid - return a Validation Exception
            raise InvalidDateException( "Invalid Date - %s" % formValue, component=self )
        


        #get the current assessment state for the component from the assessment
        state = assessment.getComponentState(self.getIdentity())
        
        #update the current state
        state.setValue(formValue)
        
        searchIndex = self.getSearchIndex()
        if searchIndex:
            state.setSearchIndex(searchIndex)
            state.setSearchValue(formValue) 
        
        #update the assessment
        assessment.setIsModified(true)

    #---------------------------------------------------------------#
    # END: Assessible Implementation                                #
    #---------------------------------------------------------------#
    
    #---------------------------------------------------------------#
    # BEGIN: KERLExtensions Implementation                          #
    #---------------------------------------------------------------#

    def compileKERLtoJavaScript(self, expression):
        """
            Returns a Python string that represents a JavaScript
            expression, which may be used to evaluate the status
            of the component class specific KERL expression.

            NOTE: this method is primarily used to produce
            JavaScript expressions that form the basis of the
            client-side showif technology.
            
            RETURN TYPE: String         
        """
        #example: cond="['datetimeselector','after', '2006-10-03']" returns 'document.getElementById("datetimeselector").value.indexOf(value)>=0'
        #example: cond="['datetimeselector','before', '2006-10-03']" returns 'document.getElementById("datetimeselector").value.indexOf(value)<0'
        #example: cond="['datetimeselector','dateis', '2006-10-03']" returns 'document.getElementById("datetimeselector").value==value'
        #example: cond="['datetimeselector','timeis', '10:10am']" returns 'document.getElementById("datetimeselector").value!=value'

        print "compileKERLtoJavaScript"

        #get the identity for each component since year, month, date, time are all in a separated field
        identity = self.getIdentity()
	yearIdentity = identity + "_year"
	monthIdentity = identity + "_month"
	dayIdentity = identity + "_day"
	hourIdentity = identity + "_hour"
	minuteIdentity = identity + "_minute"
	ampmIdentity = identity + "_ampm"

        #dateTime = d.strftime("%Y-%m-%d %I:%M %p")              
#	dateTime = year + "-" + "-" + month + "-" + day + " " + "hour" + ":" + minute + ":00 " + ampm
#	date = year + "-" + "-" + month + "-" + day + " " + "hour" + ":" + minute + ":00 " + ampm

#        storeDateTime = self.getDefaultDatetime();

#        resDateTime = DateTime(storeDateTime);
#        resDate = resDateTime.strftime("%Y-%m-%d")
#        resTime = resDateTime.strftime("%I:%M %p")              

        #get the operator
        opr = expression[1]

        #get the value
#        value = asJavaScript(expression[2])

#        print "EX2: ", expression[2]
#        print "EX1: ", expression[1]
#        print "EX0: ", expression[0]
#        print "DATE: ", DateTime(expression[2])
#        print "RESDATETIME: ", resDateTime
#        print "RESDATE: ", resDate
#        print "RESTIME: ", resTime
#        print "STOREDATE: ", storeDateTime

        dateComponent=()
        timeInExpression = ""
        hour = ""
        minute = ""
        ampm = ""
        # get value from KERL expression
        valueInExpression = expression[2]

        #get date value in show if expression
        dateInExpression = ""
        if opr in ("after","before","dateis"):
            dateObj = DateTime(valueInExpression)
        
        if opr in ("timeis"):
            timeObj = DateTime(valueInExpression)

        if opr == "after":
            print "AFTER: "
            dateExp = dateObj.strftime("%Y%m%d")
            textVal = "(parseInt(document.getElementById(\"%s\").value + document.getElementById(\"%s\").value + document.getElementById(\"%s\").value) > %s)" % (yearIdentity, monthIdentity, dayIdentity, dateExp)
            return textVal

        elif opr == "before":
            print "BEFORE: "
            dateExp = dateObj.strftime("%Y%m%d")
            textVal = "(parseInt(document.getElementById(\"%s\").value + document.getElementById(\"%s\").value + document.getElementById(\"%s\").value) < %s)" % (yearIdentity, monthIdentity, dayIdentity, dateExp)
            return textVal
        
        elif opr == "dateis":
            print "DATEIS: "
            dateExp = dateObj.strftime("%Y%m%d")
            textVal = "(parseInt(document.getElementById(\"%s\").value + document.getElementById(\"%s\").value + document.getElementById(\"%s\").value) == %s)" % (yearIdentity, monthIdentity, dayIdentity, dateExp)
            return textVal
        
        elif opr == "timeis":
            print "TIMEIS: "
            timeExp = timeObj.strftime("%I%M%p")
            textVal = "(document.getElementById(\"%s\").value + document.getElementById(\"%s\").value + document.getElementById(\"%s\").value == \"%s\")" % (hourIdentity, minuteIdentity, ampmIdentity, timeExp)
#            textVal = "(document.getElementById(\"%s\").value == \"%s\" && document.getElementById(\"%s\").value == \"%s\" && document.getElementById(\"%s\").value.toLowerCase() == \"%s\")" % (hourIdentity, asJavaScript(hour), minuteIdentity, asJavaScript(minute), ampmIdentity, asJavaScript(ampm.lower()))
            return textVal
        
        else:
            raise InvalidKerlOperator, opr
            #return "true"

    #---------------------------------------------------------------#

    def compileKERLtoEnglish(self, expression):
        """
            Returns a string representing an 'English' version
            of the component class specific KERL expression.

            NOTE: this method is primarily used to produce
            English descriptions of KERL expressions that may be 
            used to describe things like AssessmentFilters
            
            RETURN TYPE: String         
        """
        print "compileKERLtoEnglish"
        return ""

    #---------------------------------------------------------------#

    def evaluate(self, expression, assessment):
        """
            Evalutes the component class specific KERL expression
            using the provided assessment.

            NOTE: this method is primarily used to produce
            JavaScript expressions that form the basis of the
            client-side showif technology.

            RETURN TYPE: Boolean         
        """
        print "evaluate"
        print "EX0: ", expression[0]
        print "EX1: ", expression[1]
        print "EX2: ", expression[2]

        #get the identity
        identity = self.getIdentity()

        #get the operator
        opr = expression[1]

        #get the value in the show if expression
        valueInExpression = expression[2]

        #get date value in show if expression
        dateInExpression = ""
        if opr in ("after","before","dateis"):
            dateInExpression = valueInExpression

        #get time value in show if expression
        timeInExpression = ""
        if opr in ("timeis"):
            dateTimeInExpression = DateTime(valueInExpression)
            timeInExpression = dateTimeInExpression.strftime("%I:%M %p")
 
        #get the stored state
        state = assessment.getComponentState(identity)
        assessmentValue = state.getValue()

        # split datetime into separate component
        dateTimeInAssessment = DateTime(assessmentValue);
        dateInAssessment = dateTimeInAssessment.strftime("%Y-%m-%d")
        timeInAssessment = dateTimeInAssessment.strftime("%I:%M %p")              

        print "ORI VAL IN EXP VALUE: ", valueInExpression
        print "ORI VAL IN ASS VALUE ", assessmentValue
        print "ASS DATETIME OBJ: ", dateTimeInAssessment
        print "ASS DATE ", dateInAssessment
        print "ASS TIME ", timeInAssessment

        #evaluate the expression
        if opr == "after":
            # We only need to compare the date only to make sure if the date is greater then the show if date expression
            print "EV AFTER: ", DateTime(dateInAssessment) > DateTime(dateInExpression)
            return DateTime(dateInAssessment) > DateTime(dateInExpression)
        
        elif opr == "before":
            print "BEFORE: ", DateTime(dateInAssessment) < DateTime(dateInExpression)
            return DateTime(dateInAssessment) < DateTime(dateInExpression)
        
        elif opr == "dateis":
            print "DATE: ", DateTime(dateInAssessment) == DateTime(dateInExpression)
            return DateTime(dateInAssessment) == DateTime(dateInExpression)
        
        elif opr == "timeis":
            print "ASST: ", timeInAssessment
            print "STORE: ", timeInExpression
            print "TIME: ", timeInAssessment == timeInExpression
            return timeInAssessment == timeInExpression
        
        else:
            return "true"
    
    #---------------------------------------------------------------#

    def getDependencies(self, expression):
        """
            Returns a list of components that are required to
            evaluate the component class specific KERL expression.

            NOTE: Includes the specific component itself.

            RETURN TYPE: {Component} indexed by Component.getIdentity()
       """
        print "getDependencies"
        return {self.getIdentity():self}
    
    #---------------------------------------------------------------#

    def updateKERLIdentities(self, expression, identityMappings):
        """
            Returns the expression with the appropriate old identities
            (as identified by the identityMappings) replaced with 
            new identities.
        """
        #get the component identity from the expression (first element)
        identity = expression[0]
        
        #is the component in the identityMappings?
        if identityMappings.has_key(identity):
            expression[0] = identityMappings[identity]
       
        print "updateKERLIdentities" 
        return expression
         
    #---------------------------------------------------------------#    
    
    def addJavaScriptDeclarations(self, declarationsDictionary):
        """
            ABSTRACT: Adds the necessary javascript declarations to
            the declarationsDictionary which is defined as {KEY -> string}.

            The KEY is used to determine which component generated
            the declarations.  The KEY may be used to determine if
            declarations have already been generated for this type
            of component.

            The STRING is the actual javascript declaration(s).

            NOTE: When overriding, this method should always be called
            from sub-classes.
        """

        #perform the super-class additions
        Component.addJavaScriptDeclarations(self, declarationsDictionary)
       
        #add javascript declarations only if they don't already exist for this component class
        declarationsDictionary[self.getClassInfo().getClassName()] = self.JAVASCRIPT_DECLARATIONS

    def getJavascript(self):
        """ Workaround for buggy KE Javascript declaration system... """
        
        return self.JAVASCRIPT_DECLARATIONS

    #---------------------------------------------------------------#
    # END: KERLExtensions Implementation                            #
    #---------------------------------------------------------------#

#####################################################################
## Class Initialisation

# Register Component Information with the Knowledge Engine
registerGenericObjectClass(DatetimeSelector)

#####################################################################
